#! -*-coding: utf-8 -*-

"""
author:shangchun.li
time:2019-04-17
"""

from ctypes import *
import os
import sys
from binascii import unhexlify
from structureOnly import *


def singleton(cls, *args, **kw):
    instances = {}
    def _singleton():
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]
    return _singleton


class LoadDll(object):
    def __init__(self):
        self.win_dll = 0
        self.hEvent = 0
        self.hDiagPhone = 0
        self.iSpLog = 0
        self.phoneCommand = 0
        self.pLogUtil = 0

    def loaddll(self):
        """
        加载DLL文件
        :return:
        """
        try:
            abs_path = os.path.split(os.path.abspath(__file__))[0]
            # self.win_dll = windll.LoadLibrary('Kernel32.dll')
            cdll.LoadLibrary(os.path.join(abs_path, 'diagDll/Channel9.dll'))
            cdll.LoadLibrary(os.path.join(abs_path, 'diagDll/DiagChan9.dll'))
            self.iSpLog = cdll.LoadLibrary(os.path.join(abs_path, 'diagDll/iSpLog.dll'))
            self.phoneCommand = cdll.LoadLibrary(os.path.join(abs_path, 'diagDll/PhoneCommand.dll'))
        except Exception as e:
            raise Exception("load dll has except({})!".format(e))
        finally:
            pass

    def createISplog(self):
        """
        开启iSpLog日志模块
        :return:
        """
        self.pLogUtil = self.iSpLog.SpLog_CreateObject()

        log_arg = OpenArgsT()
        log_arg.module_name = 'AutoSanityBS'.encode('ascii')
        log_arg.log_level = 5
        log_arg.log_file = os.path.join(sys.path[0], 'AutoSanityBSSystem.log')
        log_arg.log_to_file = 1
        log_arg.open_flags = 0x4000 | 0x1000 | 0x2
        bOpen = self.iSpLog.SpLog_Open(self.pLogUtil, byref(log_arg))

        if bOpen is False:
            raise Exception('iSpLog::Open failed, check if the log file is opened by another application')


    def SP_CreatePhone(self):
        """
        一个操作句柄，PhoneCommand的接口调用都需要指定该句柄
        可以为各个线程创建不同的句柄实现多线程控制和调用，线程之间的句柄不可通用。
        :return:函数执行成功，无返回值。执行异常，抛出异常。
        """
        self.hDiagPhone = self.phoneCommand.SP_CreatePhone(self.pLogUtil)
        if self.hDiagPhone is None:
            raise Exception("PhoneCommand create Phone failed")

    def SP_ReleasePhone(self):
        """
        释放SP_CreatePhone创建的资源
        本函数通常在应用程序结束前调用，释放PhoneCommand资源
        :return:无返回值
        """
        self.phoneCommand.SP_ReleasePhone(self.hDiagPhone)

    def SP_BeginPhoneTestByChannelServer(self, channel_port, channel_ip):
        if isinstance(channel_port, (str, unicode)):
            if channel_port.isdigit():
                channel_port = int(channel_port)
            else:
                raise Exception("channel_port must be digit!")

        ca = ChannelServerAttriBute()
        ca.channel_type = 1  # 固定为0，CHANNEL_TYPE_SOCKET
        ca.server_port = channel_port
        ca.server_ip = channel_ip
        ca.server_flag = 0

        res = self.phoneCommand.SP_BeginPhoneTest(self.hDiagPhone, pointer(ca))
        print "res = ",res
        if res != 0:
            raise Exception('PhoneCommand::SP_BeginPhoneTestByChannelServer failed, please confirm the ip number')


    def SP_BeginPhoneTest(self, diagSerial, baudrate):
        """
        打开串口
        :param diagSerial: 串口号，int类型
        :return:无返回值
        """
        if isinstance(diagSerial, (str, unicode)):
            if diagSerial.isdigit():
                diagSerial = int(diagSerial)
            else:
                raise Exception("diagSerial must be digit!")

        ca = ChannelAttriBute()
        ca.channel_type = 0  # 固定为0，CHANNEL_TYPE_COM
        ca.port_num = diagSerial  # 在设备管理器查找SPRD LTE DIAG端口号
        ca.port_baudrate = baudrate
        res = self.phoneCommand.SP_BeginPhoneTest(self.hDiagPhone, pointer(ca))

        if res != 0:
            raise Exception('PhoneCommand::SP_BeginPhoneTest failed, please confirm the PORT number')

        return True


    def SP_EndPhoneTest(self):
        """
        关闭通讯端口。
        :return:函数执行成功，True。执行失败：False
        """
        res = self.phoneCommand.SP_EndPhoneTest(self.hDiagPhone)
        if res != 0:
            return False
        else:
            return True

    def SP_RestartPhone(self):
        """
        重启DUT
        :return:函数执行成功，True。执行失败：False
        """
        res = self.phoneCommand.SP_RestartPhone(self.hDiagPhone)
        if res != 0:
            return False
        else:
            return True

    def SP_DeepSleep(self):
        """
        控制DUT进入Deep Sleep模式
        手机一旦进入DeepSleep模式之后，无法进行任何DIAG命令通信，
        除非重启DUT重新握手进入测试模式。通常情况下在DeepSleep模式下测量漏电电流。
        :return:函数执行成功，True。执行失败：False
        """
        res = self.phoneCommand.SP_DeepSleep(self.hDiagPhone)
        if res != 0:
            return False
        else:
            return True

    def SP_PowerOff(self):
        """
        关机
        :return:函数执行成功，True。执行失败：False
        """
        res = self.phoneCommand.SP_PowerOff(self.hDiagPhone)
        if res != 0:
            return False
        else:
            return True

    def SP_GetSWVer(self,eType):
        """
        获取CP、AP的版本号
        :param eType:指定读取的版本类型，CP 或AP
        :return:(True/False,result)
        """
        str_buf = create_string_buffer(1024)
        res = self.phoneCommand.SP_GetSWVer(self.hDiagPhone, eType, pointer(str_buf), len(str_buf))
        rsbool = True if res == 0 else False
        return rsbool, repr(str_buf.value)

    def SP_LoadSN(self, snType):
        """
        从手机中读取序列号SN
        :param snType:SN1/SN2
        :return:(True/False,result)
        """
        str_buf = create_string_buffer(64)
        res = self.phoneCommand.SP_LoadSN(self.hDiagPhone, snType, pointer(str_buf), len(str_buf))
        if res != 0:
            return False, ""
        else:
            return True, repr(str_buf.value)

    def SP_UpdateSN(self, snType):
        """
        更新sn
        执行成功后，返回更新的SN
        :param snType:SN1/SN2
        :return:(True/False,result)
        """
        str_buf = create_string_buffer(64)
        res = self.phoneCommand.SP_SaveSN(self.hDiagPhone, snType, pointer(str_buf), len(str_buf))
        if res != 0:
            return False, ""
        else:
            return True, repr(str_buf.value)


    def SP_SendATCommand(self, atCmd, bWantReply, timeOut):
        """
        通过DIAG端口发送AT命令
        :param atCmd:
        :param bWantReply:bWantReply
        :return:
        """
        if not isinstance(atCmd, (str, unicode)):
            raise Exception("atCmd of SP_SendATCommand function must is string!")
        if not isinstance(bWantReply, bool):
            raise Exception("bWantReply of SP_SendATCommand function must is bool!")
        if not isinstance(timeOut, int):
            raise Exception("timeOut of SP_SendATCommand function must is int!")

        if not atCmd.endswith('\r\n') and not atCmd.endswith("\n") and not atCmd.endswith('\r') :
            atCmd = atCmd + "\r\n"
        elif not atCmd.endswith('\n') and atCmd.endswith('\r'):
            atCmd = atCmd + "\n"
        elif atCmd.endswith('\n') and not atCmd.endswith('\r'):
            atCmd.replace("\n", '\r\n')
        ARRAY = (c_ubyte * 65536)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendATCommand(self.hDiagPhone,
                                            atCmd,
                                            bWantReply,
                                            pointer(nv_data),
                                            len(nv_data),
                                            pointer(nv_len),
                                            timeOut)
        if res != 0:
            return False,None
        else:
            if bWantReply:
                x = 0
                hex_str = ""
                while x < nv_len.value:
                    h_ = str(hex(nv_data[x]))[2:]
                    if len(h_)==1:
                        h_ = "0" + h_
                    hex_str += h_
                    x += 1
                # result = unhexlify(hex_str).split("\r\n")
                # result = [x.strip() for x in result if x.strip() != ""]
                result = unhexlify(hex_str)
            else:
                result = ""

            return True, result

    def getHex(self, value):
        hex_a = 0x00
        if (value/256) > 0:
            hex_a = hex(value/256)
        hex_b = 10

    def SP_SendDiagCommand(self,x, y):
        a = [0x01, 0x64, 0x00, 0x00, x % 256, x / 256, y % 256, y / 256]  # 包含4个整数的数组类型
        64000000020001
        ARRAY = (c_int8 * 8)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        ARRAY1 = (c_ubyte * 24)  # 包含4个整数的数组类型
        nv_data1 = ARRAY1()
        for x in xrange(8):
            nv_data[x] = a[x]
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendAndRecvDiagPackage(self.hDiagPhone,
                                                          0x08, 0x00,
                                                          pointer(nv_data), 8,
                                                          0x08, 0x00,
                                                          pointer(nv_data1), len(nv_data1), pointer(nv_len), 10000)
        if res == 0:
            return True, res
        else:
            return False, res

    def SP_SendTPUpCommand(self, x, y):
        """
        通过DIAG端口向TP层发送Daig命令
        :return:
        """
        a = [0x01, 0x64, 0x00, 0x00, x%256, x/256, y%256, y/256]  # 包含4个整数的数组类型

        ARRAY = (c_int8 * 8)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        ARRAY1 = (c_ubyte * 24)  # 包含4个整数的数组类型
        nv_data1 = ARRAY1()
        for x in xrange(8):

            nv_data[x] = a[x]
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendAndRecvDiagPackage(self.hDiagPhone,
                                                   0x22, 0x00,
                                                   pointer(nv_data),8,
                                                   0x22, 0x00,
                                                          pointer(nv_data1), len(nv_data1),pointer(nv_len),10000)
        if res == 0:
            return True, res
        else:
            return False, res

    def SP_SendTPDownCommand(self, x, y):
        """
        通过DIAG端口向TP层发送Daig命令
        :return:
        """
        a = [0x02, 0x64, 0x00, 0x00, x%256, x/256, y%256, y/256]  # 包含4个整数的数组类型

        ARRAY = (c_int8 * 8)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        ARRAY1 = (c_ubyte * 24)  # 包含4个整数的数组类型
        nv_data1 = ARRAY1()
        for x in xrange(8):
            nv_data[x] = a[x]
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendAndRecvDiagPackage(self.hDiagPhone,
                                                          0x22, 0x00,
                                                          pointer(nv_data), 8,
                                                          0x22, 0x00,
                                                          pointer(nv_data1), len(nv_data1), pointer(nv_len), 10000)
        if res == 0:
            return True, res
        else:
            return False, res

    def SP_SendKeyDownCommand(self, keyword):
        """
        通过DIAG端口向TP层发送Daig命令
        :return:
        """
        a = [0x01, 0xA0, 0x08, 0x00, keyword, 0x00, 0x00, 0x00]  # 包含4个整数的数组类型

        ARRAY = (c_int8 * 8)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        ARRAY1 = (c_ubyte * 24)  # 包含4个整数的数组类型
        nv_data1 = ARRAY1()
        for x in xrange(8):
            nv_data[x] = a[x]
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendAndRecvDiagPackage(self.hDiagPhone,
                                                          0x01, 0x01,
                                                          pointer(nv_data), 8,
                                                          0x01, 0x01,
                                                          pointer(nv_data1), len(nv_data1), pointer(nv_len), 10000)
        if res == 0:
            return True,res
        else:
            return False,res

    def SP_SendKeyUpCommand(self, keyword):
        """
        通过DIAG端口向TP层发送Daig命令
        :return:
        """
        a = [0x01, 0xA0, 0x08, 0x00, keyword, 0x00, 0x00, 0x00]  # 包含4个整数的数组类型

        ARRAY = (c_int8 * 8)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        ARRAY1 = (c_ubyte * 24)  # 包含4个整数的数组类型
        nv_data1 = ARRAY1()
        for x in xrange(8):
            nv_data[x] = a[x]
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendAndRecvDiagPackage(self.hDiagPhone,
                                                          0x01, 0x02,
                                                          pointer(nv_data), 8,
                                                          0x01, 0x02,
                                                          pointer(nv_data1), len(nv_data1), pointer(nv_len), 10000)
        if res == 0:
            return True,res
        else:
            return False,res

    def SP_SendTPMoveCommand(self, x, y):
        """
        通过DIAG端口向TP层发送Daig命令
        :return:
        """
        a = [0x03, 0x64, 0x00, 0x00, x % 256, x / 256, y % 256, y / 256]  # 包含4个整数的数组类型

        ARRAY = (c_int8 * 8)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        ARRAY1 = (c_ubyte * 24)  # 包含4个整数的数组类型
        nv_data1 = ARRAY1()
        for x in xrange(8):
            nv_data[x] = a[x]
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendAndRecvDiagPackage(self.hDiagPhone,
                                                          0x22, 0x00,
                                                          pointer(nv_data), 8,
                                                          0x22, 0x00,
                                                          pointer(nv_data1), len(nv_data1), pointer(nv_len), 10000)
        if res == 0:
            return True,res
        else:
            return False,res

    def SP_SendATCommand2(self, atCmd, bWantReply, timeOut):
        """
        通过DIAG端口发送特殊AT命令
        :param atCmd:
        :param bWantReply:bWantReply
        :return:
        """
        if not isinstance(atCmd, (str, unicode)):
            raise Exception("atCmd of SP_SendATCommand function must is string!")
        if not isinstance(bWantReply, bool):
            raise Exception("bWantReply of SP_SendATCommand function must is bool!")
        if not isinstance(timeOut, int):
            raise Exception("timeOut of SP_SendATCommand function must is int!")

        ARRAY = (c_ubyte * 65536)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendATCommand(self.hDiagPhone,
                                                 atCmd,
                                                 bWantReply,
                                                 pointer(nv_data),
                                                 len(nv_data),
                                                 pointer(nv_len),
                                                 timeOut)
        if res != 0:
            return False, None
        else:
            if bWantReply:
                x = 0
                hex_str = ""
                while x < nv_len.value:
                    h_ = str(hex(nv_data[x]))[2:]
                    if len(h_) == 1:
                        h_ = "0" + h_
                    hex_str += h_
                    x += 1
                # result = unhexlify(hex_str).split("\r\n")
                # result = [x.strip() for x in result if x.strip() != ""]
                result = unhexlify(hex_str)
            else:
                result = ""

            return True, result

    def SP_apGetBatteryCapacity(self):
        """
        读取电池电量
        :return:
        """
        c_l = c_ulong()
        res = self.phoneCommand.SP_apGetBatteryCapacity(self.hDiagPhone, pointer(c_l))
        if res != 0:
            return False, None
        else:
            return True, c_l.value

    def SP_gsmLoadCalFlag(self):
        """
        读取GSM/TD校准标志位
        :return:
        """
        c_l = c_ulong()
        res = self.phoneCommand.SP_gsmLoadCalFlag(self.hDiagPhone, pointer(c_l))
        print
        if res != 0:
            return False, None
        else:
            return True, c_l.value

    def SP_wcdmaLoadCalFlag(self):
        """
        读取WCDMA校准标志位
        :return:
        """
        c_l = c_long()
        res = self.phoneCommand.SP_wcdmaLoadCalFlag(self.hDiagPhone, pointer(c_l))
        if res != 0:
            return False, None
        else:
            return True, c_l.value

    def SP_lteLoadCalFlag(self):
        """
        读取LTE校准标志位
        :return:
        """
        c_l = c_ulong()
        res = self.phoneCommand.SP_lteLoadCalFlag(self.hDiagPhone, pointer(c_l))
        print
        if res != 0:
            return False, None
        else:
            return True, c_l.value

    def SP_EnableArmLog(self, enable):
        """
        开启modem PS trace输出
        :return:
        """
        if not isinstance(enable, bool):
            raise Exception("enable of SP_EnableArmLog function must is bool!")
        res = self.phoneCommand.SP_EnableArmLog(self.hDiagPhone,enable)

        if res != 0:
            return False, None
        else:
            return True,None

    def SP_AssertUE(self):
        """
        开启Assert Mode
        :return:
        """
        res = self.phoneCommand.SP_AssertUE(self.hDiagPhone)
        if res != 0:
            return False, None
        else:
            return True, None

    def SP_PressKeyboard(self, keyCode):
        """

        :param keyCode:
        :return:
        """
        if not isinstance(keyCode, (int, long)):
            raise Exception("keyCode of SP_PressKeyboard function must is int!")
        res = self.phoneCommand.SP_PressKeyboard(self.hDiagPhone, keyCode)
        if res != 0:
            return False, None
        else:
            return True, None


    def SP_VibrateOn(self,enable):
        """
        震动开关
        :param enable:
        :return:
        """
        if not isinstance(enable, bool):
            raise Exception("enable of SP_VibrateOn function must is bool!")
        res = self.phoneCommand.SP_VibrateOn(self.hDiagPhone, enable)
        if res != 0:
            return False, None
        else:
            return True, None

    def SP_ReadNV(self, nvId):
        """
        读取指定ID的NV数据。
        :param nvId: Nv ID
        :return:
        """
        array = (c_ubyte * 65536)  # 包含4个整数的数组类型
        nv_data = array()
        nv_len = c_ulong()
        res = self.phoneCommand.SP_ReadNV(self.hDiagPhone,
                                          nvId,
                                          pointer(nv_data),
                                          len(nv_data),
                                          pointer(nv_len))
        result = []
        if res != 0:
            raise False, result
        else:
            x = 0
            while x < nv_len.value:
                result.append("0x%X" % (nv_data[x]))
                x += 1
            return True, result

    def SP_Write(self,commant):
        array = (c_char * 2)
        nv_data = array()
        nv_data[0] = commant
        nv_data[1] = '\n'
        ret = self.phoneCommand.SP_Write(self.hDiagPhone,pointer(nv_data),2)
        if ret == 0:
            return True
        else:
            return False

    def SP_Read(self):
        array = (c_char * 65536)  # 包含4个整数的数组类型
        nv_data = array()
        ret = self.phoneCommand.SP_Read(self.hDiagPhone, pointer(nv_data), len(nv_data),1000)
        if ret == 0:
            x = 0
            st = ""
            while x < len(nv_data):
                st += nv_data[x]
                x += 1
            return True, st
        else:
            return False, None

    def SP_SendASBSIMNewCommand(self, cmd):
        # 发送ASBSIM新增命令
        if not isinstance(cmd, list):
            raise Exception("cmd is not a list")
        if len(cmd) < 1:
            return False, "cmd is null!"

        length = len(cmd)
        ARRAY = (c_int8 * length)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        ARRAY1 = (c_ubyte * 65536)  # 包含4个整数的数组类型
        nv_data1 = ARRAY1()
        for x in xrange(length):
            nv_data[x] = cmd[x]
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendAndRecvDiagPackage(self.hDiagPhone,
                                                          0x62, 0x00,
                                                          pointer(nv_data), length,
                                                          0x62, 0x00,
                                                          pointer(nv_data1), len(nv_data1), pointer(nv_len), 10000)
        if res != 0:
            return False, None
        else:
            x = 0
            hex_str = ""
            while x < nv_len.value:
                h_ = str(hex(nv_data1[x]))[2:]
                if len(h_) == 1:
                    h_ = "0" + h_
                hex_str += h_
                x += 1
            # 前8个字符为AP_CMD和length，去除
            if len(hex_str) >= 8:
                hex_str = hex_str[8:]
            result = unhexlify(hex_str)
            if result.__contains__("SUC"):
                return True, result
            else:
                return False, result

    def SP_SendCustomDiagCommand(self, cmd, type, subtype):
        # 发送自定义的diag命令
        if not isinstance(cmd, list):
            raise Exception("cmd is not a hex list")
        if len(cmd) < 1:
            return False, "cmd is null!"
        if not isinstance(type, str):
            raise Exception("type must be String")
        if not isinstance(subtype, str):
            raise Exception("subtype must be String")


        length = len(cmd)
        ARRAY = (c_int8 * length)  # 包含4个整数的数组类型
        nv_data = ARRAY()
        ARRAY1 = (c_ubyte * 65536)  # 包含4个整数的数组类型
        nv_data1 = ARRAY1()
        for x in xrange(length):
            nv_data[x] = cmd[x]
        nv_len = c_ulong()
        res = self.phoneCommand.SP_SendAndRecvDiagPackage(self.hDiagPhone,
                                                          int(type, 16), int(subtype, 16),
                                                          pointer(nv_data), length,
                                                          int(type, 16), int(subtype, 16),
                                                          pointer(nv_data1), len(nv_data1), pointer(nv_len), 10000)
        if res != 0:
            return False, None
        else:
            x = 0
            hex_str = ""
            while x < nv_len.value:
                h_ = str(hex(nv_data1[x]))[2:]
                if len(h_) == 1:
                    h_ = "0" + h_
                hex_str += h_
                x += 1
            result = unhexlify(hex_str)
            return result